import _ from 'lodash';
import { document as document$1 } from 'ssr-window';

/**
 * 智能化还原的XML-JSON转换器
 * @Author: wenjm 
 * @Date: 2018-03-27 01:38:28 
 * @Last Modified by: wenjm
 * @Last Modified time: 2018-04-12 11:04:03
 */
class XmlParser {
    _setXml(xml) {
        if (xml && typeof xml == "string") {
            this.xml = document$1.createElement("div");
            this.xml.innerHTML = xml;
            this.xml = this.xml.getElementsByTagName("*")[0];
        }
        else if (typeof xml == "object") {
            this.xml = xml;
        }
    }
    _isAsProperty(xml) {
        if (xml.parentNode && xml.parentNode.children) {
            let group = _.groupBy(xml.parentNode.children, function (t) { return t.nodeName; });
            if (group) {
                let result = false;
                _.each(group, t => {
                    if (_.isArray(t) && t.length > 1) {
                        result = true;
                        return false;
                    }
                });
                return result;
            }
        }
        return false;
    }
    _isAsArray(xml) {
        if (xml.hasChildNodes() && xml.childNodes.length > 0) {
            let result = true;
            let name;
            _.each(xml.children, t => {
                let tempname = t.nodeName;
                if (t.attributes && t.attributes.name && t.attributes.name.nodeValue) {
                    tempname += t.attributes.name.nodeValue;
                }
                if (name && name != tempname) {
                    result = false;
                    return false;
                } else {
                    name = tempname;
                }
            });
            return result;
        }
        return false;
    }
    _trim(value) {
        let temp = value.trim();
        temp = _.trim(temp, '"');
        temp = _.trim(temp, '”');
        return temp;
    }
    _format(value) {
        if (value == undefined) {
            return value;
        }
        let temp = value;
        if (_.isString(temp)) {
            let reg = RegExp(/\[CDATA\[[\s\S]*\]\]/);
            if (reg.test(temp)) {
                temp = temp.substring(temp.indexOf('[CDATA[') + 7, temp.lastIndexOf(']]'));
            }
            let encodeReg = RegExp("^[a-zA-z0-9.*_%-]*$");
            if (encodeReg.test(temp)) {
                temp = decodeURIComponent(temp);
            }
            temp = this._trim(temp);
        }
        let reg = RegExp(/^[-]{0,1}[0-9]*[.]{0,1}[0-9]*$/);
        if (reg.test(temp)) {
            let number = parseFloat(temp);
            if (temp == number.toString()) {
                temp = number;
            }
        }
        else if (Date.parse(temp)) {
            temp = new Date(Date.parse(temp));
        }
        if (temp === 'true' || temp === 'false') {
            temp = temp === 'true';
        }
        return temp;
    }
    _convertToJSON(xml) {
        if (xml.nodeType != 1) {
            return null;
        }
        let obj = {};
        let root = obj[xml.nodeName.toLowerCase()] = {};
        if (this._isAsArray(xml)) {
            root = obj[xml.nodeName.toLowerCase()] = [];
        }
        let nodeValue = (xml.textContent || "").replace(/(\r|\n)/g, "").replace(/^\s+|\s+$/g, "");
        if (_.isEmpty(nodeValue) && xml.childNodes.length == 1 && xml.childNodes[0].nodeName == "#comment") {
            nodeValue = xml.childNodes[0].nodeValue;
        }
        if (_.isEmpty(nodeValue) && xml.nextSibling && xml.nextSibling.nodeType === 3) {
            nodeValue = xml.nextSibling.nodeValue;
            if (!xml.hasChildNodes()) {
                let key = xml.nodeName.toLowerCase();
                obj[this._trim(key)] = this._format(nodeValue);
            }
        }

        let appendProperty = false;

        if (nodeValue && xml.childNodes.length == 1 && !xml.childNodes[0].hasChildNodes()) {
            let key = xml.nodeName.toLowerCase();
            if (xml.attributes.name && xml.attributes.name.nodeValue && this._isAsProperty(xml)) {
                obj._key = this._format(key);
                key = xml.attributes.name.nodeValue;
                appendProperty = true;
            }
            obj[this._trim(key)] = this._format(nodeValue);
        }
        if (xml.attributes.length > 0) {
            for (let j = 0; j < xml.attributes.length; j++) {
                let attribute = xml.attributes.item(j);
                if (attribute.nodeName == "name" && appendProperty) {
                    continue;
                }
                obj[this._trim(attribute.nodeName)] = this._format(attribute.nodeValue);
            }
        }
        if (xml.childNodes.length > 0) {
            for (let i = 0; i < xml.childNodes.length; i++) {
                let node = xml.childNodes.item(i);
                let item = this._convertToJSON(node);
                if (item == undefined) {
                    continue;
                }
                if (_.isArray(root)) {
                    if (Object.keys(item).length == 1) {
                        root._key = Object.keys(item)[0];
                        item = item[root._key];
                    }
                    root.push(item);
                } else {
                    _.extend(root, item);
                }
            }
        }

        if (appendProperty && _.isEmpty(root)) {
            delete obj[xml.nodeName.toLowerCase()];
        }

        let current = obj[xml.nodeName.toLowerCase()];
        if (xml.children.length == 0 && (!_.isDate(current) && _.isObject(current) && _.isEmpty(current))) {
            obj[xml.nodeName.toLowerCase()] = undefined;
        }

        return obj;
    }
    toJSON(xml) {
        this._setXml(xml);
        return this._convertToJSON(this.xml);
    }
    _convertToXML(obj) {
        let reg = RegExp(/[&<>\"'\\/]/);
        for (let key in obj) {
            if (obj._key && !_.isArray(obj) && !_.isArray(obj[key])) {
                if ('_key' === key) {
                    continue;
                }
                this.result.push("<{0} name=\"{1}\">".format(obj._key, key));
            } else {
                this.result.push("<" + key + ">");
            }
            let item = obj[key];
            if (_.isArray(item)) {
                item.forEach(t => {
                    let temp = {};
                    temp[item._key || 'item'] = t;
                    this._convertToXML(temp);
                });
            }
            else if (_.isObject(item)) {
                this._convertToXML(item);
            } else if (_.isString(item) && reg.test(item)) {
                this.result.push("< ![CDATA[" + item + "]] >");
            } else {
                this.result.push(item);
            }
            if (obj._key && !_.isArray(obj)) {
                this.result.push("</" + obj._key + ">");
            } else {
                this.result.push("</" + key + ">");
            }
        }
    }
    toXML(json, encoding = "GBK") {
        this.result = [];
        this.result.push("<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>");
        this._convertToXML(json);
        return this.result.join("");
    }
}

export default XmlParser;

